package com.blog.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.blog.common.domain.dto.ArticleStatusDto;
import com.blog.common.domain.vo.TagVo;
import com.blog.common.mapper.dao.ArticleMapper;
import com.blog.common.mapper.dao.CategoryMapper;
import com.blog.common.mapper.dao.FileInfoMapper;
import com.blog.common.mapper.dao.TagMapper;
import com.blog.admin.service.ArticleService;
import com.blog.admin.service.ArticleTagService;
import com.blog.admin.service.TagService;
import com.blog.common.core.exception.ServiceException;
import com.blog.common.core.utils.PageUtils;
import com.blog.common.core.web.page.TableDataInfo;
import com.blog.common.domain.dto.ArticleDto;
import com.blog.common.domain.dto.ArticleTopDto;
import com.blog.common.domain.dto.ConditionDto;
import com.blog.common.domain.entity.*;
import com.blog.common.domain.vo.ArticleBackVo;
import com.blog.common.domain.vo.ArticleInfoVo;
import com.blog.common.redis.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jacoco.agent.rt.internal_43f5073.core.internal.flow.IFrame;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

import static com.blog.common.core.constant.CacheConstants.*;
import static com.blog.common.core.constant.ServiceConst.DEFAULT_CATEGORY_ID;
import static com.blog.common.domain.enums.ArticleEnums.DRAFT;

/**
 * <p>
 * 文章 服务实现类
 * </p>
 *
 * @author Valerie
 * @since 2023-03-11
 */
@Service
@Slf4j
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    @Resource
    private ArticleMapper articleMapper;
    @Resource
    private RedisService redisService;

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private TagService tagService;


    @Resource
    private TagMapper tagMapper;

    @Resource
    private ArticleTagService articleTagService;

    @Resource
    private FileInfoMapper fileInfoMapper;

    @Override
    public TableDataInfo<ArticleBackVo> listArticleBacks(ConditionDto condition) {
        List<ArticleBackVo> articleBackVoList = articleMapper.listArticleBacks(PageUtils.getLimitPageSize(), PageUtils.getPageSize(), condition);
        if (CollectionUtil.isEmpty(articleBackVoList)) {
            return new TableDataInfo<>();
        }
        Map<Object, Double> likeCountMap = redisService.zGetAll(ARTICLE_LIKE_COUNT);
        Map<Object, Double> viewCountMap = redisService.zGetAll(ARTICLE_VIEWS_COUNT);
        articleBackVoList = articleBackVoList.stream().peek(item -> {
            // 设置点赞数
            Long likeCount = Optional.ofNullable(likeCountMap.get(item.getId())).orElse(0.00).longValue();
            // 设置浏览量
            Long viewCount = Optional.ofNullable(viewCountMap.get(item.getId())).orElse( 0.00).longValue();
            item.setLikeCount(likeCount);
            item.setViewsCount(viewCount);
        }).collect(Collectors.toList());

        return new TableDataInfo<>(articleBackVoList, articleBackVoList.size());
    }

    @Override
    public ArticleInfoVo getArticleBackById(Serializable articleId) {
        ArticleInfoVo articleInfoVo = articleMapper.getArticleInfoById(articleId);
        if (ObjUtil.isNull(articleInfoVo)){
            throw new ServiceException("文章不存在 ！");
        }
        return articleInfoVo;
    }



    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateArticleBackTop(ArticleTopDto articleTopDto) {
        return new LambdaUpdateChainWrapper<>(articleMapper)
                .eq(Article::getId, articleTopDto.getId())
                .set(Article::getIsTop, articleTopDto.getIsTop())
                .update();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateArticle(ArticleDto articleDto) {
        Category category = saveArticleCategory(articleDto);
        Article article = BeanUtil.copyProperties(articleDto, Article.class);
        if (ObjUtil.isNotNull(category)){
            article.setCategoryId(category.getId());
        }
        if (StringUtils.isNotBlank(articleDto.getArticleCover())){
            FileInfo fileInfo;
            fileInfo = fileInfoMapper.selectOne(new LambdaQueryWrapper<FileInfo>()
                    .eq(FileInfo::getFilePath, article.getArticleCover())
                    .last("limit 1")
            );
            if (Objects.isNull(fileInfo)){
                fileInfo = FileInfo.builder()
                        .id(IdUtil.getSnowflakeNextId())
                        .filePath(articleDto.getArticleCover())
                        .build();
                fileInfoMapper.insert(fileInfo);
                redisService.hPut(FILE_INFO, fileInfo.getId().toString(), fileInfo.getFilePath());
            }
            article.setArticleCover(String.valueOf(fileInfo.getId()));
        }
        // todo 用户id
        this.saveOrUpdate(article);
        saveArticleTag(articleDto, article.getId());
        log.info("保存成功->文章标题：{}", article.getArticleTitle());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteArticle(Serializable articleIds) {
        List<String> articleIdList = Arrays.asList(articleIds.toString().split(","));
        this.remove(new LambdaQueryWrapper<Article>().in(Article::getId, articleIdList));
        articleTagService.remove(new LambdaQueryWrapper<ArticleTag>().in(ArticleTag::getArticleId, articleIdList));
        log.info("删除文章-> {}", articleIdList);
    }

    /**
     * 保存分类
     *
     * @param articleDto  文章
     * @return Category     分类
     */
    private Category saveArticleCategory(ArticleDto articleDto){
        if (articleDto.getCategoryId() != null){
            return null;
        }
        // 判断是否存在标签
        Category category = new LambdaQueryChainWrapper<>(categoryMapper)
                .eq(Category::getCategoryName, articleDto.getCategoryName())
                .one();
        if (StringUtils.isBlank(articleDto.getCategoryName())){
            category = new LambdaQueryChainWrapper<>(categoryMapper)
                    .eq(Category::getId, DEFAULT_CATEGORY_ID)
                    .one();
        }
        if (ObjectUtil.isNull(category) && !DRAFT.getStatus().equals(articleDto.getStatus())){
            // 不存在且文章不是草稿
            category = Category.builder()
                    .categoryName(articleDto.getCategoryName())
                    .build();
            // 插入
            categoryMapper.insert(category);
        }
        return category;
    }

    /**
     * 保存标签
     *
     * @param articleDto  文章
     * @param articleId 文章id
     * @return void
     */
    private void saveArticleTag(ArticleDto articleDto, Long articleId){
        // 删除旧的标签
        if (articleDto.getId() != null){
            articleTagService.remove(new LambdaQueryWrapper<ArticleTag>()
                    .eq(ArticleTag::getArticleId, articleDto.getId()));
        }
        List<TagVo> tagVoList = articleDto.getTagVoList();
        List<String> tagNameList = tagVoList.stream().filter(f -> f.getId() == null).map(TagVo::getTagName).collect(Collectors.toList());
        Map<String, Long> tagMap = new LambdaQueryChainWrapper<>(tagMapper)
                .in(CollectionUtil.isNotEmpty(tagNameList), Tag::getTagName, tagNameList)
                .list()
                .stream()
                .filter(f -> f.getId() != null)
                .collect(Collectors.toMap(Tag::getTagName, Tag::getId, (k1, k2) -> k2));
        tagVoList = tagVoList.stream()
                .peek(p -> p.setId(
                        Optional.ofNullable(p.getId()).orElse(
                        Optional.ofNullable(tagMap.get(p.getTagName())).orElse(IdUtil.getSnowflakeNextId())
                )))
                .collect(Collectors.toList());
        List<Tag> tagList = BeanUtil.copyToList(tagVoList, Tag.class);
        tagService.saveOrUpdateBatch(tagList);
        List<ArticleTag> articleTagList = tagVoList.stream().limit(3).map(item -> ArticleTag.builder()
                .tagId(item.getId())
                .articleId(articleId)
                .build())
                .collect(Collectors.toList());
        articleTagService.saveBatch(articleTagList);
    }

    @Override
    public boolean recommendArticle(ArticleStatusDto articleStatusDto) {

        return new LambdaUpdateChainWrapper<>(articleMapper)
                .eq(Article::getId, articleStatusDto.getArticleId())
                .set(null != articleStatusDto.getIsTop(), Article::getIsTop, articleStatusDto.getIsTop())
                .set(null != articleStatusDto.getIsRecommend(), Article::getIsRecommend, articleStatusDto.getIsRecommend())
                .update();
    }
}
